import pandas as pd
from openpyxl.utils.dataframe import dataframe_to_rows
from openpyxl.styles import Alignment
from tkinter.filedialog import askopenfilename, asksaveasfilename
from tkinter.messagebox import showerror, showinfo
import tkinter as tk
import logging
import time

# --- 配置区域 (硬编码内容提取) ---
# 设置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

# 仪器名称和列顺序配置
INSTRUMENT_COLUMN_ORDERS = {
    '大天平': ['分析日期', '任务单号', '检测项目', '称量物品', '是否零点', '分析人'],
    '烘箱': ['分析日期', '任务单号', '使用温度', '仪器状况', '分析人', '检测项目'],
    '紫外': ['任务单号', '检测项目', '波长', '仪器状况', '分析人', '分析日期'],
    '恒温恒湿系统': ['分析日期', '任务单号', '检测项目', '使用温度', '使用湿度', '分析人'],
    '恒温恒湿箱': ['分析日期', '任务单号', '检测项目', '使用温度', '使用湿度', '分析人'],
    '小天平': ['分析日期', '任务单号', '检测项目', '称量物品', '是否零点', '分析人'],
    '气相色谱仪苯': ['分析日期', '任务单号', '检测项目', '仪器状况', '分析人'],
    '气相色谱仪非': ['分析日期', '任务单号', '检测项目', '仪器状况', '分析人'],
    'COD消解仪': ['任务单号', '检测项目', '分析日期', '分析人', '样品数量'],
    'pH计': ['分析日期', '任务单号', '仪器状况', '分析人'],
    '水浴锅': ['分析日期', '任务单号', '检测项目', '使用温度', '仪器状况', '分析人'],
    '浊度计': ['分析日期', '任务单号', '零点', '量程', '是否校准', '分析人'],
    '原子荧光仪': ['任务单号', '检测项目', '仪器状况', '分析人', '分析日期'],
    '红外分光光度计': ['分析日期', '任务单号', '样品数量', '仪器状况', '分析人','检测项目'],
    '气相色谱质谱仪': ['分析日期', '任务单号', '检测项目', '仪器状况', '分析人'],
    '高压锅': ['分析日期', '任务单号', '检测项目', '使用温度', '仪器状况', '分析人'],
    '生化培养箱': ['分析日期', '任务单号', '检测项目', '使用温度', '仪器状况', '分析人'],
    '原子吸收分光光度计': ['任务单号', '检测项目', '使用方法', '仪器状况', '分析人', '分析日期'],
    '离子计': ['分析日期', '任务单号', '检测项目', '仪器状况', '分析人'],
    '电加热板': ['任务单号', '检测项目', '分析日期', '分析人', '样品数量'],
    '电热恒温培养箱': ['分析日期', '任务单号', '检测项目', '使用温度', '仪器状况', '分析人'],
    '箱式电阻炉': ['分析日期', '任务单号', '使用温度', '仪器状况', '分析人', '检测项目'],
    '新天平': ['分析日期', '任务单号', '检测项目', '称量物品', '是否零点', '分析人']
}

# 仪器默认值配置
INSTRUMENT_DEFAULTS = {
    '大天平': {'称量物品': '/', '是否零点': '\u2611是 □否'},
    '烘箱': {'使用温度': '105℃', '仪器状况': '正常'},
    '紫外': {'仪器状况': '正常'},
    '恒温恒湿系统': {'使用温度': '20', '使用湿度': '50'},
    '恒温恒湿箱': {'使用温度': '20', '使用湿度': '50'},
    '小天平': {'称量物品': '/', '是否零点': '\u2611是 □否'},
    '气相色谱仪苯': {'仪器状况': '正常'},
    '气相色谱仪非': {'仪器状况': '正常'},
    'pH计': {'仪器状况': '正常'},
    '水浴锅': {'使用温度': '100℃', '仪器状况': '正常'},
    '浊度计': {'零点': '/', '量程': '/', '是否校准': '是'},
    '原子荧光仪': {'仪器状况': '正常'},
    '红外分光光度计': {'仪器状况': '正常'},
    '气相色谱质谱仪': {'仪器状况': '正常'},
    '高压锅': {'使用温度': '120℃', '仪器状况': '正常'},
    '生化培养箱': {'使用温度': '44.5℃', '仪器状况': '正常'},
    '原子吸收分光光度计': {'使用方法': '/', '仪器状况': '正常'},
    '离子计': {'仪器状况': '正常'},
    '电热恒温培养箱': {'使用温度': '37℃', '仪器状况': '正常'},
    '箱式电阻炉': {'使用温度': '800℃', '仪器状况': '正常'},
    '新天平': {'称量物品': '/', '是否零点': '\u2611是 □否'},
}

# 特殊处理仪器列表
SPECIAL_HANDLING_INSTRUMENTS = {
    '气相色谱仪非', '红外分光光度计', '原子吸收分光光度计'
}

# 需要调整日期的仪器
DATE_ADJUSTMENT_INSTRUMENTS = {'恒温恒湿系统'}

# 任务单号简化前缀长度
TASK_NUMBER_PREFIX_LENGTH = 4

# --- 函数定义区域 ---

def simplify_task_numbers(task_numbers):
    """简化任务单号"""
    if pd.isna(task_numbers) or task_numbers == '':
        return '/'
    all_task_numbers = str(task_numbers).split('、')
    valid_task_numbers = [num for num in all_task_numbers if len(num) == 9 and '/' not in num]
    invalid_task_numbers = [num for num in all_task_numbers if len(num) != 9 or '/' in num]

    if not valid_task_numbers:
        simplified = ','.join(invalid_task_numbers) if invalid_task_numbers else '/'
        return simplified if simplified else '/'

    prefix = valid_task_numbers[0][:TASK_NUMBER_PREFIX_LENGTH]
    suffixes = sorted([num[-5:] for num in valid_task_numbers], key=int)

    simplified_suffixes = []
    start = None
    for i, num in enumerate(suffixes):
        if start is None:
            start = num
        elif int(num) - int(suffixes[i - 1]) == 1:
            continue
        else:
            end = suffixes[i - 1]
            simplified_suffixes.append(f"{start}-{end}" if start != end else start)
            start = num

    if start is not None:
        end = suffixes[-1]
        simplified_suffixes.append(f"{start}-{end}" if start != end else start)

    simplified_valid_part = f"{prefix}{','.join(simplified_suffixes)}"
    simplified_invalid_part = ','.join(invalid_task_numbers)

    if simplified_invalid_part:
        result = f"{simplified_valid_part},{simplified_invalid_part}"
    else:
        result = simplified_valid_part

    return result if result else '/'


def process_special_instrument(instrument_df, instrument_name):
    """处理需要特殊分组聚合逻辑的仪器"""
    try:
        if instrument_name == '气相色谱仪非':
            first_grouped_df = instrument_df.groupby(['分析日期', "任务单号"], as_index=False).agg({
                '样品数量': "sum",
                '检测项目': lambda x: ','.join(set('非甲烷总烃' if item in ['非甲烷总烃（有组织）', '非甲烷总烃（无组织）',"非甲烷总烃（有组织3）","非甲烷总烃（无组织3）"] else item for item in x)),
                '分析人': 'first'
            })
            grouped_df = first_grouped_df.groupby(['分析日期', "检测项目"], as_index=False).agg({
                '任务单号': lambda x: '、'.join(str(item) for item in set(x)),
                '样品数量': "sum",
                '分析人': 'first'
            })

        elif instrument_name == '红外分光光度计':
            first_grouped_df = instrument_df.groupby(['分析日期', "检测项目"], as_index=False).agg({
                '任务单号': lambda x: '、'.join(str(item) for item in x),
                '样品数量': "sum",
                '分析人': 'first'
            })
            grouped_df = first_grouped_df.groupby(['分析日期', '任务单号'], as_index=False).agg({
                '检测项目': lambda x: '、'.join(str(item) for item in x),
                '样品数量': "sum",
                '分析人': 'first'
            })

        elif instrument_name == '原子吸收分光光度计':
            first_grouped_df = instrument_df.groupby(['分析日期', '检测项目'], as_index=False).agg({
                '样品数量': "sum",
                '任务单号': lambda x: '、'.join(str(item) for item in x),
                '分析人': 'first'
            })
            grouped_df = first_grouped_df.groupby(['分析日期', '任务单号'], as_index=False).agg({
                '检测项目': lambda x: '、'.join(str(item) for item in x),
                '样品数量': "sum",
                '分析人': 'first'
            })
        else:
            # 理论上不会到达这里，因为由调用方保证
            grouped_df = pd.DataFrame()
        return grouped_df
    except Exception as e:
        logging.error(f"处理特殊仪器 {instrument_name} 数据时出错: {e}")
        return pd.DataFrame() # Return empty DataFrame on error

def process_standard_instrument(instrument_df):
    """处理标准分组聚合逻辑的仪器"""
    try:
        grouped_df = instrument_df.groupby(['检测项目', '分析日期'], as_index=False).agg({
            '任务单号': lambda x: '、'.join(str(item) for item in x),
            '样品数量': "sum",
            '波长': 'first',
            '分析人': 'first'
        })
        return grouped_df
    except Exception as e:
        logging.error(f"处理标准仪器数据时出错: {e}")
        return pd.DataFrame() # Return empty DataFrame on error

def create_and_write_worksheet(writer, instrument_name, instrument_df, column_orders, instrument_defaults):
    """为单个仪器创建并写入工作表"""
    try:
        if instrument_df.empty:
            worksheet = writer.book.create_sheet(title=instrument_name)
            logging.info(f"仪器 {instrument_name} 无数据，创建空工作表。")
            return

        # 日期处理
        instrument_df.loc[:, '分析日期'] = pd.to_datetime(instrument_df['分析日期'], errors='coerce')
        if instrument_name in DATE_ADJUSTMENT_INSTRUMENTS and not instrument_df.empty:
            instrument_df.loc[:, '分析日期'] -= pd.DateOffset(days=1)

        # 分组聚合
        if instrument_name in SPECIAL_HANDLING_INSTRUMENTS:
            grouped_df = process_special_instrument(instrument_df, instrument_name)
        else:
            grouped_df = process_standard_instrument(instrument_df)

        if grouped_df.empty:
             worksheet = writer.book.create_sheet(title=instrument_name)
             logging.warning(f"仪器 {instrument_name} 分组后无数据，创建空工作表。")
             return

        # 创建结果DataFrame
        result_df = pd.DataFrame(columns=column_orders[instrument_name])
        for column in grouped_df.columns:
            if column in result_df.columns:
                result_df[column] = grouped_df[column]

        # 填充默认值
        defaults = instrument_defaults.get(instrument_name, {})
        for column, default_value in defaults.items():
            if column not in result_df.columns:
                 result_df[column] = default_value
            else:
                result_df[column] = default_value

        # 排序和格式化
        result_df = result_df.sort_values(by='分析日期', ascending=True)
        # 替换原代码
        result_df['分析日期'] = result_df['分析日期'].dt.strftime('%Y年%m月%d日')
        result_df.loc[:, '任务单号'] = result_df['任务单号'].apply(simplify_task_numbers)

        # 写入 Excel
        worksheet = writer.book.create_sheet(title=instrument_name)
        rows = dataframe_to_rows(result_df, index=False, header=True)
        for row_index, r in enumerate(rows, 1): # openpyxl rows are 1-indexed
            worksheet.append(r)
            for cell in worksheet[row_index]: # Access row by index
                cell.alignment = Alignment(horizontal='center', vertical='center')

        # 调整列宽
        for column_cells in worksheet.columns:
            try:
                max_content_length = max(len(str(cell.value)) for cell in column_cells if cell.value is not None)
                max_header_length = len(str(column_cells[0].value)) if column_cells[0].value is not None else 0
                column_width = max(max_content_length, max_header_length) + 10
                worksheet.column_dimensions[column_cells[0].column_letter].width = min(column_width, 100) # 限制最大宽度
            except Exception as e:
                 logging.warning(f"调整列宽时出错 ({instrument_name}, {column_cells[0].column_letter}): {e}")

        logging.info(f"仪器 {instrument_name} 数据处理并写入完成。")

    except Exception as e:
        logging.error(f"为仪器 {instrument_name} 创建/写入工作表时出错: {e}")
        # 可以选择在这里创建一个空表或标记错误
        try:
            worksheet = writer.book.create_sheet(title=f"{instrument_name}_Error")
            worksheet.append(["处理此仪器数据时发生错误", str(e)])
        except:
            pass # 如果连错误表都创建不了，就忽略

# --- 主程序流程 ---

def main():
    """主程序入口"""
    root = tk.Tk()
    root.withdraw()

    # 选择源文件
    source_file_path = askopenfilename(title="选择源文件", filetypes=[("Excel files", "*.xlsx")])
    if not source_file_path:
        showerror("错误", "未选择源数据文件，请检查。")
        return

    try:
        df = pd.read_excel(source_file_path)
        # 过滤掉 '使用仪器名称' 为 '/' 或空字符串的行
        df['样品数量'] = pd.to_numeric(df['样品数量'], errors='coerce').fillna(0).astype(int)
        df = df[(df['使用仪器名称'] != '/') & (df['使用仪器名称'] != "") & (df['使用仪器名称'].notna())]
        if df.empty:
            showerror("错误", "源文件中没有有效的仪器使用记录。")
            return
    except FileNotFoundError:
        showerror("错误", "源文件不存在，请检查文件路径。")
        return
    except pd.errors.ParserError as e:
        showerror("错误", f"解析源文件时出错: {e}")
        return
    except Exception as e:
        showerror("错误", f"读取源文件时发生未知错误: {e}")
        return

    # 选择保存路径
    new_file_path = asksaveasfilename(title="选择保存新文件的路径", defaultextension=".xlsx",
                                      filetypes=[("Excel files", "*.xlsx")])
    if not new_file_path:
        showerror("错误", "未选择保存路径，请检查。")
        return

    try:
        with pd.ExcelWriter(new_file_path, engine='openpyxl', mode='w') as writer:
            # 处理所有在配置中定义的仪器
            for instrument in INSTRUMENT_COLUMN_ORDERS.keys():
                # --- 关键修正：使用精确的列表分割和匹配 ---
                # 创建一个布尔掩码，用于判断每一行的'使用仪器名称'是否包含当前的instrument
                def contains_instrument(row_instrument_names):
                    # 将字符串按顿号分割成列表，并去除首尾空格
                    instrument_list = [name.strip() for name in str(row_instrument_names).split('、')]
                    return instrument in instrument_list

                # 应用函数创建掩码
                mask = df['使用仪器名称'].apply(contains_instrument)
                instrument_df_specific = df[mask].copy() if mask.any() else pd.DataFrame()
                # ---

                create_and_write_worksheet(
                    writer,
                    instrument,
                    instrument_df_specific,
                    INSTRUMENT_COLUMN_ORDERS,
                    INSTRUMENT_DEFAULTS
                )

        showinfo("成功", "操作成功，数据已保存到新文件。")
        logging.info("程序执行成功。")

    except Exception as e:
        logging.exception("写入文件时发生错误")  # Logs the full traceback
        showerror("错误", f"保存文件时发生错误: {e}")


if __name__ == "__main__":
    main()
    print("\n程序将在 5 秒后关闭...")
    time.sleep(5)




